package controllers.admin

import controllers.{AssetsFinder, checkAdmin}
import models.tasks.JobInfo

import javax.inject.Inject
import play.api.Logging
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.{JsArray, Json}
import play.api.mvc.{AbstractController, BodyParsers, ControllerComponents}
import services.OutStockPostParameters
import tasks.{DynamicJobSchedule, LayuiJobExecution}

import scala.concurrent.{ExecutionContext, Future}

class TasksController @Inject()(task: DynamicJobSchedule,
                                cc: ControllerComponents
                               )(implicit exec: ExecutionContext, parser: BodyParsers.Default)
  extends AbstractController(cc)
    with play.api.i18n.I18nSupport with Logging {
  val taskForm: Form[TesksForm] = Form {
    mapping(
      "id" -> text,
      "taskname" -> nonEmptyText,
      "cron" -> optional(text),
      "description" -> optional(text),
      "status" -> optional(text),
    )(TesksForm.apply)(TesksForm.unapply)
  }

  val parameters = Form(
    mapping(
      "page" -> number,
      "limit" -> number,
      "sdate" -> default(text, ("")),
      "status" -> default(number, (0)),
      "p1" -> default(text, ("")),
      "p2" -> default(text, ("")),

    )(OutStockPostParameters.apply)(OutStockPostParameters.unapply)
  )

  def index() = checkAdmin.async { implicit request =>
    task.findAllJob.map(f => Ok(views.html.tasks.index("task_index", f.toList)))
  }

  def indexPost() = checkAdmin.async { implicit request =>
    println(request.body.asJson)
    val place = request.body.asJson.getOrElse(Json.obj("page" -> 1, "limit" -> 10))
    (place \ "option").asOpt[String].getOrElse("") match {
      case "create" => Future(Ok((s"create\n${(place \ "id").toString}")))
      case "edit" => Future(Ok((s"edit\n${(place \ "id").toString}")))
      case "delete" => {
        (place \ "id").as[JsArray].value.foreach(id => task.deleteJob(id.as[String]))
        Future(Ok((s"delete\n${(place \ "id").toString}")))
      }
      case "onoff" => {
        (place \ "id").as[JsArray].value.foreach(f => task.loadJobs((f \ "id").as[String], !(f \ "status").as[Boolean]))
        Future(Ok((s"onoff\n${(place \ "id").toString}")))
      }
      case _ => task.findAllJob.map { f =>
        val data = f.map { r =>
          s"""{
             |"id":"${r.id}",
             |"name":"${r.name}",
             |"cronExpression":"${r.cronExpression.getOrElse("")}",
             |"next_run_time":"${r.next_run_time.getOrElse("").toString}",
             |"description":"${r.description.getOrElse("")}",
             |"status":${r.status}
             |}""".stripMargin
        }
        Ok(Json.obj("code" -> 0, "message" -> "", "count" -> data.length, "data" -> Json.parse("[" + data.toArray.mkString(",") + "]")))
      }
    }
  }

  def editTask(id: String = "") = checkAdmin.async { implicit request =>
    task.findJob(id).map(f => Ok(views.html.tasks.edit(taskForm, f, task.getAllJobName)))
  }

  def addTask() = checkAdmin.async { implicit request =>
    taskForm.bindFromRequest().fold(
      errorForm => Future.successful(Ok(views.html.tasks.edit(errorForm, JobInfo(name = "")))),
      t => {
        println(t)
        task.saveJob(id = t.id, name = t.name, cronExpression = t.cronExpression, description = t.description).map { r =>
          Redirect(controllers.admin.routes.TasksController.index()).flashing("success" -> (s"Add Task(${r.toString})"))
        }
      }
    )
  }

  def jobExecutions() = checkAdmin.async { implicit request =>
    task.jobExecutionsName.map(names =>
      Ok(views.html.tasks.jobex("jobex", names.toList))
    )
  }

  def listJobExecutions() = checkAdmin.async { implicit request =>
    val p: OutStockPostParameters = parameters.bindFromRequest().fold(
      formWithErrors => {
        println("is application/badform")
        BadRequest(formWithErrors.toString)
        OutStockPostParameters()
      },
      f => {
        println("is application/form")
        //implicit val formDataWrite = Json.writes[OutStockPostParameters]
        OutStockPostParameters(f.page - 1, f.limit, f.sdate, f.status, f.p1, f.p2)
      }
    )
    println(s"listJobExecutions form = ${p.toString()}")
    val res = task.jobExecutionsList(p)
    implicit val tableResJson = Json.format[LayuiJobExecution]
    res.map { page =>
      val data = page._1.map(i =>
        LayuiJobExecution(
          id = i.id.getOrElse(0),
          status = {
            i.status match {
              case 0 => "未知";
              case 1 => "开始";
              case 2 => "完成";
              case _ => "错误";
            }
          },
          started = i.started.get.toLocalDateTime.toString,
          finished = {
            if (i.finished.nonEmpty) i.finished.get.toLocalDateTime.toString else ""
          },
          exception = i.exception.getOrElse(""),
          traceback = i.traceback.getOrElse(""),
          job_id = i.job_id,
          name = i.name,
          runtime = if (i.finished.nonEmpty) "%.3f".format((i.finished.get.getTime - i.started.get.getTime) / 1000f) else ""
        ))
      Ok(Json.obj("code" -> 0, "message" -> "", "count" -> page._2, "data" -> Json.toJson(data)))
    }
  }

  def removeJobExecutions() = checkAdmin.async { implicit request =>
    println(request.body.asJson)
    val place = request.body.asJson.getOrElse(Json.obj("page" -> 1, "limit" -> 10))
    try {
      if ((place \ "key").as[String] == "id") {
        val idArr = (place \ "id").as[JsArray].value.toArray
        for (id <- idArr) {
          task.jobExecutionsRemove(id.asOpt[Int])
        }
        Future(Ok(s"delete(${idArr.length})"))
      } else {
        task.jobExecutionsRemove().map(f => Ok(s"delete(${f})"))
      }
    } catch {
      case e: Exception => {
        Future(Ok("delete error"))
      }
    }
  }

}

case class TesksForm(id: String = "", name: String, cronExpression: Option[String] = None, description: Option[String] = None, status: Option[String] = None)